package cn.superchart.web.core.config;
import cn.superchart.common.properties.SecurityProperties;
import cn.superchart.common.utils.Checker;
import cn.superchart.module.menu.service.ScMenuService;
import cn.superchart.web.core.handler.ScJwtAuthenticationTokenHandler;
import cn.superchart.web.core.handler.ScAccessDeniedHandler;
import cn.superchart.web.core.handler.ScAuthenticationEntryPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Arrays;
import java.util.Collections;
import java.util.Map;

import static org.springframework.security.config.Customizer.withDefaults;

@Configuration
@EnableWebSecurity
public class SpringSecurityConfig {

    @Autowired
    private SecurityProperties securityProperties;

    @Autowired
    private ScMenuService scMenuService;

    private final ScJwtAuthenticationTokenHandler scJwtAuthenticationTokenHandler;
    private final ScAuthenticationEntryPoint scAuthenticationEntryPoint;
    private final ScAccessDeniedHandler scAccessDeniedHandler;

    public SpringSecurityConfig(ScJwtAuthenticationTokenHandler scJwtAuthenticationTokenHandler, ScAuthenticationEntryPoint scAuthenticationEntryPoint, ScAccessDeniedHandler scAccessDeniedHandler) {
        this.scJwtAuthenticationTokenHandler = scJwtAuthenticationTokenHandler;
        this.scAuthenticationEntryPoint = scAuthenticationEntryPoint;
        this.scAccessDeniedHandler = scAccessDeniedHandler;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }


    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.cors(withDefaults())
                //这一行代码是用来禁用Cross-Site Request Forgery (CSRF)保护的。
                // CSRF是一种网络攻击方式，通过禁用它，意味着在当前安全配置中，我们不检查请求是否携带了CSRF令牌。
                // 这通常在API接口或状态无关(stateless)应用中是安全的做法，因为这些场景下CSRF攻击的风险较低
                .csrf(csrf->csrf.disable())
                //这里配置了会话管理策略为STATELESS（无状态）。
                // 这意味着Spring Security不会创建或使用HTTP会话来跟踪用户的状态。
                // 这对于RESTful API服务特别有用，因为每个请求都应该携带所有必要的认证信息（如JWT令牌），而不是依赖于服务器端的会话。
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                //这一配置指定了对任何HTTP请求都不进行权限检查，允许所有的请求自由通过。
                // 这是一种非常宽松的安全策略，通常在开发阶段或者特定的公开接口上使用。
                // 在生产环境中，应根据实际情况更细致地控制不同请求的访问权限。
                //添加jwt过滤器
                .authorizeHttpRequests(auth -> {
                    urlPerm(auth);
                    // 如果白名单不为空，先允许白名单中的路径
                    if (Checker.BeNotEmpty(securityProperties.getWhiteList())) {
                        // 先放行白名单中的路径
                        auth.requestMatchers(securityProperties.getWhiteList()).permitAll();
                    }
                    // 对其它请求进行认证
                    auth.anyRequest().authenticated();
                }).addFilterBefore(scJwtAuthenticationTokenHandler, UsernamePasswordAuthenticationFilter.class)
                //配置异常处理器
                .exceptionHandling(exception -> exception.authenticationEntryPoint(scAuthenticationEntryPoint).accessDeniedHandler(scAccessDeniedHandler));
                return http.build();
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
        return configuration.getAuthenticationManager();
    }

    @Bean
    public SecurityContextLogoutHandler securityContextLogoutHandler() {
        return new SecurityContextLogoutHandler();
    }


    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
       // configuration.setAllowedOrigins(Collections.singletonList("*")); // 允许的域名
        configuration.setAllowedOriginPatterns(Collections.singletonList("*"));
        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); // 允许的请求方法
        configuration.setAllowedHeaders(Collections.singletonList("*")); // 允许的请求头
        configuration.setAllowCredentials(true); // 允许携带 Cookie
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration); // 应用于所有路径
        return source;
    }

    private void urlPerm(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry auth){
        Map<String,String> permMap = scMenuService.urlPerm();
        if(Checker.BeNotEmpty(permMap)){
            for(String key:permMap.keySet()){
                auth.requestMatchers(key).hasAuthority(permMap.get(key));
            }
        }
    }
}
